﻿using System;
using System.Collections.Generic;
using Maria.Common.Aspects;
using Maria.Common.Provider;

namespace Maria.Common.EF
{
    /// <summary>
    /// Плагин к MSSQL-базам для DAOTransaction
    /// В отличии от nhibernate-ового DAOProvider, на каждом уровне свой контекст (см. паттерн Unit of work)
    /// </summary>
    public abstract class EntityContextImpl<T> : ContextHolder<Stack<T>>, IDAOTransactionExtension
        where T : IDisposable
    {
        public object Enter()
        {
            var holder = GetHolder();
            if (holder == null)
            {
                holder = new Stack<T>();
                SetHolder(holder);
            }
            var context = CreateContext();
            holder.Push(context);
            return context;
        }

        public void Leave(object cookie, bool commit)
        {
            var holder = GetHolder();
            if (holder == null || holder.Count == 0)
                throw new ApplicationException("Контекст не открыт");

            var topContext = holder.Pop();
            try
            {
                if (!ReferenceEquals(topContext, cookie))
                    throw new ApplicationException("Запутались в контекстах");

                if (commit)
                {
                    Commit(topContext);
                }
            }
            finally 
            {
                topContext.Dispose();
            }
        }

        public abstract T CreateContext();
        public abstract void Commit(T Context);

        public static T Get
        {
            get
            {
                var holder = GetHolder();
                if (holder == null || holder.Count == 0)
                    throw new ApplicationException("Контекст не открыт");
                return holder.Peek();
            }
        }
    }

    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)]
    [LinkToWrapper(typeof(DAOTransactionAttribute))]
    //[LinkToWrapper(typeof(FakeTransactionAttribute))]
    public abstract class EntityContextAttribute : WrapperParameter, IDAOTransactionExtension
    {
        // Этот изврат сделан из-за того, что атрибуты нельзя наследовать от Generic-классов
        protected IDAOTransactionExtension Impl;

        public object Enter()
        {
            return Impl.Enter();
        }

        public void Leave(object cookie, bool commit)
        {
            Impl.Leave(cookie, commit);
        }
    }
}
